package spring.framework.beans.factory.support;

import spring.framework.beans.BeansException;
import spring.framework.beans.factory.DisposableBean;
import spring.framework.beans.factory.ObjectFatory;
import spring.framework.beans.factory.config.SingletonBeanRegistry;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    /**
     * Internal marker for a null singleton object:
     * used as marker value for concurrent Maps (which don't support null values).
     */
    protected static final Object NULL_OBJECT = new Object();

    /**
     * 一级缓存，普通对象
     * Cache of sinleton Objects:bean Name --> bean Instance
     */
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    /**
     * 二级缓存， 提前暴露对象，没有完全实例化的对象
     * Cache of early sinleton objects: bean name --> bean instance
     */
    private final Map<String, Object> earlySinletonObjects = new HashMap<>();
    /**
     * 三级缓存， 存放的代理对象
     * Cache of sinleton fatories: bean name --> ObjectFactory
     */
    private final Map<String, ObjectFatory<?>> sinletonFactories = new HashMap<>();

    private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();

    /**
     * 获取单例对象：
     * 1、从一级缓存拿
     * 2、一级缓存卫康：去二级缓存拿
     * 3、二级缓存为空：去三级缓存拿
     * 4、三级缓存如有：拿出来并放入二级缓存，并在三级缓存remove这个单例
     * 5、返回实例
     */
    @Override
    public Object getSingleton(String beanName) {
        Object singletonObject = singletonObjects.get(beanName);
        if (singletonObject == null) {
            singletonObject = earlySinletonObjects.get(beanName);
            if (singletonObject == null) {
                ObjectFatory<?> sinletonFactory = sinletonFactories.get(beanName);
                if (sinletonFactory != null) {
                    singletonObject = sinletonFactory.getObject();
                    earlySinletonObjects.put(beanName, singletonObject);
                    sinletonFactories.remove(beanName);
                }
            }
        }
        return singletonObject;
    }

    /**
     * 注册单例对象：
     * 1、放入一级缓存
     * 2、清空二级缓存
     * 3、清空三级缓存
     */
    public void registerSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
        earlySinletonObjects.remove(beanName);
        sinletonFactories.remove(beanName);
    }

    protected void addSingletonFactory(String beanName, ObjectFatory<?> sinletonFactory) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.sinletonFactories.put(beanName, sinletonFactory);
            this.earlySinletonObjects.remove(beanName);
        }
    }

    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
            }
        }
    }

}
